<?php

/**
 * @version $Id: files.php 1111 2005-11-19 00:26:46Z Jinx $
 * @package Joomla
 * @copyright Copyright (C) 2005 Open Source Matters. All rights reserved.
 * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
 * Joomla! is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 * See COPYRIGHT.php for copyright notices and details.
 */
//Modifed for use with JCE Editor Core and plugins - Ryan Demmer ryandemmer@gmail.com 05/03/2006
//To be removed in migration to Joomla 1.5
/** boolean True if a Windows based host */
define('JPATH_ISWIN', (substr(PHP_OS, 0, 3) == 'WIN'));

if (!defined('DS')) {
    /** string Shortcut for the DIRECTORY_SEPERATOR define */
    define('DS', DIRECTORY_SEPARATOR);
}

if (!defined('JPATH_ROOT')) {
    /** string The root directory of the file system in native format */
    define('JPATH_ROOT', JPath::clean($mosConfig_absolute_path));
}

/**
 * A File handling class
 *
 * @package Joomla
 * @static
 * @since 1.1
 */
class JFile {

    /**
     * Count the number of files in a given folder
     */
    static function countFiles($path) {
        $total = 0;
        if (JFolder::exists($path)) {
            $files = JFolder::files($path);
            $total = count($files);
            foreach ($files as $file) {
                if (strtolower($file) == 'index.html' || strtolower($file) == 'thumbs.db') {
                    $total = $total - 1;
                }
            }
        }
        return $total;
    }

    /**
     * Gets the extension of a file name
     * @param string The file name
     * @return string
     */
    static function getExt($file) {
        $dot = strrpos($file, '.') + 1;
        return strtolower(substr($file, $dot));
    }

    /**
      /**
     * Strips the last extension off a file name
     * @param string The file name
     * @return string
     */
    static function stripExt($file) {
        return preg_replace('#\.[^.]*$#', '', $file);
    }

    /**
     * Makes file name safe to use
     * @param string The name of the file (not full path)
     * @return string The sanitised string
     */
    function makeSafe($file) {
        $search = array('#[^A-Za-z0-9\.\_\-\s]#', '#\s#');
        $replace = array('', '_');
        return strtolower(preg_replace($search, $replace, $file));
    }

    function getDate($file) {
        return JCEUtils::formatDate(@filemtime($file));
    }

    function getSize($file) {
        return JCEUtils::formatSize(@filesize($file));
    }

    /**
     * Rename a file
     * @param string The path to the source file
     * @param string The path to the destination file
     * @param string An optional base path to prefix to the file names
     * @return mixed
     */
    static function copy($src, $dest) {

        global $cl;
        $error = false;

        JPath::check($src);
        JPath::check($dest);

        if (JFile::exists($src)) {
            if (is_writable(dirname($dest))) {
                if (!@JFile::exists($dest)) {
                    if (!@copy($src, $dest)) {
                        $error = $cl['copy_err'];
                    }
                } else {
                    $error = $cl['file_exists_err'];
                }
            } else {
                $error = $cl['dir_write_err'];
            }
        } else {
            $error = $cl['no_source'];
        }
        return $error;
    }

    /**
     * Rename a file
     * @param string The path to the source file
     * @param string The path to the destination file
     * @param string An optional base path to prefix to the file names
     * @return mixed
     */
    static function rename($src, $dest) {

        global $cl;
        $error = false;

        JPath::check($src);
        JPath::check($dest);

        if (JFile::exists($src)) {
            if (is_writable(dirname($dest))) {
                if (!@JFile::exists($dest)) {
                    if (!@rename($src, $dest)) {
                        $error = $cl['ren_err'];
                    }
                } else {
                    $error = $cl['file_exists_err'];
                }
            } else {
                $error = $cl['dir_write_err'];
            }
        } else {
            $error = $cl['no_source'];
        }
        return $error;
    }

    /**
     * Delete a file
     * @param mixed The file name or an array of file names
     * @return boolean  True on success
     */
    static function delete($file) {
        if (is_array($file)) {
            $files = $file;
        } else {
            $files[] = $file;
        }

        $failed = 0;
        foreach ($files as $file) {
            $file = JPath::clean($file, false);
            JPath::check($file);
            $failed |= ! unlink($file);
        }
        return!$failed;
    }

    /**
     * @param string The full file path
     * @param string The buffer to read into
     * @return boolean True on success
     */
    static function read($file, &$buffer) {
        JPath::check($file);

        if (file_exists($file)) {
            $buffer = file_get_contents($file);
            return true;
        }
        return false;
    }

    /**
     * @param string The full file path
     * @param string The buffer to write
     * @return mixed The number of bytes on success, false otherwise
     */
    static function write($file, $buffer) {
        JPath::check($file);

        if (!is_writable($file)) {
            if (!is_writable(dirname($file))) {
                return false;
            }
        }
        return file_put_contents($file, $buffer);
    }

    static function createCopy($dir, $file) {
        $filename = basename($file);
        $ext = JFile::getExt($filename);
        $base = JFile::stripExt($filename);
        $filename = $base . '_copy' . '.' . $ext;

        return $filename;
    }

    /*
     * @param string The name of the php (temporary) uploaded file
     * @param string The name of the file to put in the temp directory
     * @param string The message to return
     */

    static function upload($srcFile, $destFile, $overwrite) {
        global $cl;
        $error = false;

        //$srcFile = JPath::clean( $srcFile, false );
        //$destFile = JPath::clean( $destFile, false );
        JPath::check($destFile);

        $baseDir = dirname($destFile);

        if (!$overwrite) {
            while (JFile::exists($destFile)) {
                $destFile = JPath::makePath($baseDir, JFile::createCopy($baseDir, $destFile));
            }
        }
        //File does not exist or file exists and overwrite is true
        if (!JFile::exists($destFile) || ( JFile::exists($destFile) && $overwrite )) {
            $destFile = $destFile;
        }
        //File exists, overwrite & unique are false, return error
        if (JFile::exists($destFile) && !$overwrite) {
            $error = $cl['upload_exists_err'];
        } else {
            if (JFolder::exists($baseDir)) {
                if (is_writable($baseDir)) {
                    if (move_uploaded_file($srcFile, $destFile)) {
                        if (JPath::setPermissions($destFile)) {
                            $error = $destFile;
                        } else {
                            $error = $cl['upload_perm_err'];
                        }
                    } else {
                        $error = $cl['upload_err'];
                    }
                } else {
                    $error = $cl['upload_dest_err'];
                }
            } else {
                $error = $cl['upload_dest_err2'];
            }
        }
        return $error;
    }

    /** Wrapper for the standard file_exists function
     * @param string filename relative to installation dir
     * @return boolean
     */
    static function exists($file) {
        $file = JPath::clean($file, false);
        return is_file($file);
    }

}

/**
 * A Folder handling class
 *
 * @package Joomla
 * @static
 * @since 1.1
 */
class JFolder {

    /**
     * Count the number of folders in a given folder
     */
    static function countDirs($path) {
        $total = 0;
        if (JFolder::exists($path)) {
            $folders = JFolder::folders($path);
            $total = count($folders);
        }
        return $total;
    }

    /**
     * @param string A path to create from the base path
     * @param int Directory permissions
     * @return boolean True if successful
     */
    static function create($path = '', $mode = 0755) {
        global $mainframe;

        JPath :: check($path);
        $path = JPath :: clean($path, false);

        // Check if dir already exists
        if (JFolder :: exists($path)) {
            return true;
        }
        // First set umask
        $origmask = @ umask(0);

        // We need to get and explode the open_basedir paths
        $obd = ini_get('open_basedir');

        // If open_basedir is et we need to get the open_basedir that the path is in
        if ($obd != null) {
            if (JPATH_ISWIN) {
                $obdSeparator = ";";
            } else {
                $obdSeparator = ":";
            }
            // Create the array of open_basedir paths
            $obdArray = explode($obdSeparator, $obd);
            $inOBD = false;
            // Iterate through open_basedir paths looking for a match
            foreach ($obdArray as $test) {
                if (!(strpos($path, $test) === false)) {
                    $obdpath = $test;
                    $inOBD = true;
                    break;
                }
            }

            if ($inOBD == false) {
                // Return false for JFolder::create because the path to be created is not in open_basedir
                return false;
            }
        }

        // Just to make sure
        $inOBD = true;

        do {
            $dir = $path;

            while (!@ mkdir($dir, $mode)) {
                $dir = dirname($dir);

                if ($obd != null) {
                    if (strpos($dir, $obdpath) === false) {
                        $inOBD = false;
                        break 2;
                    }
                }
                if ($dir == '/' || is_dir($dir))
                    break;
            }
        }
        while ($dir != $path);

        // Reset umask
        @ umask($origmask);

        // If there is no open_basedir restriction this should always be true
        if ($inOBD == false) {
            // Return false for JFolder::create -- could not create path without violating open_basedir restrictions
            $ret = false;
        } else {
            $ret = true;
        }
        return $ret;
    }

    /**
     * Delete a folder
     * @param mixed The folder name
     * @return boolean True on success
     */
    static function delete($path) {
        $path = JPath::clean($path, false);
        JPath::check($path);

        // Remove all the files in folder
        $files = JFolder :: files($path, '.', false, true);
        JFile :: delete($files);

        // Remove sub-folders of folder
        $folders = JFolder :: folders($path, '.', false, true);
        foreach ($folders as $folder) {
            JFolder :: delete($folder);
        }
        // remove the folders
        return rmdir($path);
    }

    /**
     * Rename a folder
     * @param string The path to the source folder
     * @param string The path to the destination folder
     * @return mixed
     */
    static function rename($src, $dest) {

        global $cl;
        $error = false;

        JPath::check($src);
        JPath::check($dest);

        if (JFolder::exists($src)) {
            if (is_writable(dirname($dest))) {
                if (!@JFolder::exists($dest)) {
                    if (!@rename($src, $dest)) {
                        $error = $cl['ren_err'];
                    }
                } else {
                    $error = $cl['dir_exists_err'];
                }
            } else {
                $error = $cl['dir_write_err'];
            }
        } else {
            $error = $cl['no_source'];
        }
        return $error;
    }

    /** Wrapper for the standard file_exists function
     * @param string filename relative to installation dir
     * @return boolean
     */
    static function exists($path) {
        $path = JPath::clean($path, false);
        return is_dir($path);
    }

    /**
     * Utility function to read the files in a directory
     * @param string The file system path
     * @param string A filter for the names
     * @param boolean Recurse search into sub-directories
     * @param boolean True if to prepend the full path to the file name
     * @return array
     */
    static function files($path, $filter='.', $recurse=false, $fullpath=false) {
        $arr = array();
        $path = JPath::clean($path, false);
        if (!is_dir($path)) {
            return $arr;
        }

        // prevent snooping of the file system
        //JPath::check( $path );
        // read the source directory
        $handle = opendir($path);
        $path .= DIRECTORY_SEPARATOR;
        while ($file = readdir($handle)) {
            $dir = $path . $file;
            $isDir = is_dir($dir);
            if ($file <> '.' && $file <> '..') {
                if ($isDir) {
                    if ($recurse) {
                        $arr2 = JFolder::files($dir, $filter, $recurse, $fullpath);
                        $arr = array_merge($arr, $arr2);
                    }
                } else {
                    if (preg_match("/" . $filter . "/", strtolower($file))) {
                        if ($fullpath) {
                            $arr[] = $path . $file;
                        } else {
                            $arr[] = $file;
                        }
                    }
                }
            }
        }
        closedir($handle);
        natcasesort($arr);
        return array_values($arr);
    }

    /**
     * Utility function to read the folders in a directory
     * @param string The file system path
     * @param string A filter for the names
     * @param boolean Recurse search into sub-directories
     * @param boolean True if to prepend the full path to the file name
     * @return array
     */
    static function folders($path, $filter='.', $recurse=false, $fullpath=false) {
        $arr = array();
        $path = JPath::clean($path, false);
        if (!is_dir($path)) {
            return $arr;
        }

        // prevent snooping of the file system
        //mosFS::check( $path );
        // read the source directory
        $handle = opendir($path);
        $path .= DIRECTORY_SEPARATOR;
        while ($file = readdir($handle)) {
            $dir = $path . $file;
            $isDir = is_dir($dir);
            if (( $file <> '.' ) && ( $file <> '..' ) && ( $file <> 'jce_cache' ) && $isDir) {
                // removes CVS directores from list
                if (preg_match("/$filter/", $file) && !( preg_match("/CVS/", $file) )) {
                    if ($fullpath) {
                        $arr[] = $dir;
                    } else {
                        $arr[] = $file;
                    }
                }
                if ($recurse) {
                    $arr2 = JFolder::folders($dir, $filter, $recurse, $fullpath);
                    $arr = array_merge($arr, $arr2);
                }
            }
        }
        closedir($handle);
        natcasesort($arr);
        return array_values($arr);
    }

    /**
     * Lists folder in format suitable for tree display
     */
    static function listFolderTree($path, $filter, $maxLevel=999, $level=0, $parent=0) {
        $dirs = array();
        if ($level == 0) {
            $GLOBALS['_JFolder_folder_tree_index'] = 0;
        }

        if ($level < $maxLevel) {
            JPath::check($path);

            $folders = JFolder::folders($path, $filter);

            // first path, index foldernames
            for ($i = 0, $n = count($folders); $i < $n; $i++) {
                $id = ++$GLOBALS['_JFolder_folder_tree_index'];
                $name = $folders[$i];
                $fullName = JPath::clean($path . '/' . $name, false);
                $dirs[] = array(
                    'id' => $id,
                    'parent' => $parent,
                    'name' => $name,
                    'fullname' => $fullName,
                    'relname' => str_replace(JPATH_ROOT, '', $fullName)
                );
                $dirs2 = JFolder::listFolderTree($fullName, $filter, $maxLevel, $level + 1, $id);
                $dirs = array_merge($dirs, $dirs2);
            }
        }
        return $dirs;
    }

    /**
     * New folder base function. A wrapper for the JFolder::create function
     * @param string $folder The folder to create
     * @return boolean true on success
     */
    static function createFolder($folder, $mode=0755) {
        if (@JFolder::create($folder, $mode)) {
            $html = "<html>\n<body bgcolor=\"#FFFFFF\">\n</body>\n</html>";
            $file = $folder . "/index.html";
            @JFile::write($file, $html);
        } else {
            return false;
        }
        return true;
    }

}

/**
 * A Path handling class
 * @package Joomla
 * @since 1.1
 */
class JPath {

    /**
     * Append a / to the path if required.
     * @param string $path the path
     * @return string path with trailing /
     */
    static function fixPath($path) {
        //append a slash to the path if it doesn't exists.
        if (!( substr($path, -1) == '/' ))
            $path .= '/';
        return $path;
    }

    /**
     * Concat two paths together. Basically $pathA+$pathB
     * @param string $pathA path one
     * @param string $pathB path two
     * @return string a trailing slash combinded path.
     */
    static function makePath($pathA, $pathB) {
        $pathA = JPath::fixPath($pathA);
        if (substr($pathB, 0, 1) == '/')
            $pathB = substr($pathB, 1);

        return $pathA . $pathB;
    }

    /**
     * Checks if a files permissions can be changed
     * @param string The file path
     * @return boolean
     */
    static function canCHMOD($file) {
        $perms = fileperms($file);
        if ($perms !== false)
            if (@chmod($file, $perms ^ 0001)) {
                @chmod($file, $perms);
                return true;
            }
        return false;
    }

    /**
     * Chmods files and directories recursivly to given permissions
     *
     * @param string $path Root path to begin changing mode [without trailing slash]
     * @param string $filemode Octal representation of the value to change file mode to [null = no change]
     * @param string $foldermode Octal representation of the value to change folder mode to [null = no change]
     * @return boolean True if successful [one fail means the whole operation failed]
     * @since 1.1
     */
    static function setPermissions($path) {
        return mosChmod($path);
    }

    /**
     * Get the permissions of the file/folder at a give path
     *
     * @param string $path The path of a file/folder
     * @return string Filesystem permissions
     * @since 1.1
     */
    static function getPermissions($path) {
        $path = JPath::clean($path, false);
        JPath::check($path);
        $mode = @ decoct(@ fileperms($path) & 0777);

        if (strlen($mode) < 3) {
            return '---------';
        }
        $parsed_mode = '';
        for ($i = 0; $i < 3; $i++) {
            // read
            $parsed_mode .= ( $mode {
                    $i }
                    & 04) ? "r" : "-";
            // write
            $parsed_mode .= ( $mode {
                    $i }
                    & 02) ? "w" : "-";
            // execute
            $parsed_mode .= ( $mode {
                    $i }
                    & 01) ? "x" : "-";
        }
        return $parsed_mode;
    }

    /**
     * Checks for snooping outside of the file system root
     * @param string A file system path to check
     */
    static function check($path) {
        if (strpos($path, '..') !== false) {
            JCEUtils::BackTrace();
            die('JPath::check use of relative paths not permitted'); // don't translate
        }
        if (strpos(JPath::clean($path), JPATH_ROOT) !== 0) {
            JCEUtils::BackTrace();
            die('JPath::check snooping out of bounds @ ' . $path); // don't translate
        }
    }

    /**
     * Function to strip additional / or \ in a path name
     * @param string The path
     * @param boolean Add trailing slash
     */
    static function clean($p_path, $p_addtrailingslash=true) {
        $retval = '';
        $path = trim($p_path);

        if (empty($p_path)) {
            $retval = JPATH_ROOT;
        } else {
            if (JPATH_ISWIN) {
                $retval = str_replace('/', DIRECTORY_SEPARATOR, $p_path);
                // Remove double \\
                $retval = str_replace('\\\\', DIRECTORY_SEPARATOR, $retval);
            } else {
                $retval = str_replace('\\', DIRECTORY_SEPARATOR, $p_path);
                // Remove double //
                $retval = str_replace('//', DIRECTORY_SEPARATOR, $retval);
            }
        }
        if ($p_addtrailingslash) {
            if (substr($retval, -1) != DIRECTORY_SEPARATOR) {
                $retval .= DIRECTORY_SEPARATOR;
            }
        }

        return $retval;
    }

}

//Utilities Class
class JCEUtils {

    /**
     * Format the file size, limits to Mb.
     * @param int $size the raw filesize
     * @return string formated file size.
     */
    static function formatSize($size) {
        if ($size < 1024)
            return $size . ' bytes';
        else if ($size >= 1024 && $size < 1024 * 1024)
            return sprintf('%01.2f', $size / 1024.0) . ' Kb';
        else
            return sprintf('%01.2f', $size / (1024.0 * 1024)) . ' Mb';
    }

    /**
     * Format the date.
     * @param int $date the unix datestamp
     * @return string formated date.
     */
    static function formatDate($date) {
        return date("d/m/Y,H:i", $date);
    }

    /**
     * Format a backtrace error
     * @since 1.1
     */
    static function BackTrace() {
        if (function_exists('debug_backtrace')) {
            echo '<div align="left">';
            foreach (debug_backtrace () as $back) {
                if (@$back['file']) {
                    echo '<br />' . str_replace(JPATH_ROOT, '', $back['file']) . ':' . $back['line'];
                }
            }
            echo '</div>';
        }
    }

}

?>
